package models

import (
	"gogogo/services/dtos"
	"strconv"
	"strings"

	"gorm.io/gorm"
)

const (
	TYPE_FACEBOOK = 1
	TYPE_WECHAT   = 2
	TYPE_TOURIST  = 99
)

type Result struct {
	RandomId int // 驼峰对应sql里的snake case,如RandomId 对应数据库的 random_id
}

type ResultEx struct {
	Id int
}

func transferObject(d *dtos.UserDetail, u *User) {
	d.Id = u.Id
	d.Playerid = u.Playerid
	d.Manorlv = u.Manorlv
	d.Manorexp = u.Manorexp
	d.Coin = u.Coin
	d.Openid = u.Openid
	d.Type = u.Type
	d.Uname = u.Uname
	d.Pwd = u.Pwd
	d.Headurl = u.Headurl
	d.Headicon = u.Headicon
	d.Star = u.Star
	d.Energy = u.Energy
	d.Stone = u.Stone
}

func (db *DbWrapper) TouristLogin(uuid string) (*User, []*dtos.UserBuildingX) {
	resp := new(dtos.UserDetail)
	u1 := new(User)
	db.DbInstance.Where("openid=? and type=99", uuid).Find(&u1)
	if u1.Id == 0 {
		// do not exist
		// generate one
		var result Result
		db.DbInstance.Raw(`SELECT random_id
		FROM (
		  SELECT FLOOR(RAND() * 99999) AS random_id 
		  FROM users
		  UNION
		  SELECT FLOOR(RAND() * 99999) AS random_id
		) AS ss
		WHERE "random_num" NOT IN (SELECT playerid FROM users)
		LIMIT 1`).Scan(&result)

		if result.RandomId == 0 {
			return nil, nil
		}
		parameterConfigs := new(ParameterConfigs)
		db.DbInstance.First(&parameterConfigs)
		energy:=parameterConfigs.Staminamax
		// playerid += 20000000
		user := &User{Uname: uuid, Pwd: "", Openid: uuid, Type: TYPE_TOURIST, Playerid: result.RandomId, Manorlv: 1, Skilllv: 1,Energy:energy,Slotsfreetime:0}
		db.DbInstance.Create(&user)
		// 查询无前置建筑的建筑给到新用户
		var building_cfg []BuildingConfigs
		db.DbInstance.Where("pre='0'").Find(&building_cfg)
		for i := 0; i < len(building_cfg); i++ {
			userBuilding1 := &UserBuilding{
				Uid:        user.Id,
				Buildingid: building_cfg[i].Id,
				Step:       1,
				Skin:       0,
				Showskin:   0,
				Totalstep:  building_cfg[i].Steps,
			}
			db.DbInstance.Create(&userBuilding1)
			resp.UserBuilding = append(resp.UserBuilding, &dtos.UserBuildingX{
				Buildingid: building_cfg[i].Id, Id: userBuilding1.Id, Step: 1, Totalstep: building_cfg[i].Steps,
			})
		}
		// transferObject(resp, user)
		return user, resp.UserBuilding
	} else {
		var buildings []UserBuilding
		db.DbInstance.Where("uid=?", u1.Id).Find(&buildings)
		for i := 0; i < len(buildings); i++ {
			resp.UserBuilding = append(resp.UserBuilding, &dtos.UserBuildingX{
				Buildingid: buildings[i].Buildingid,
				Id:         buildings[i].Id,
				Totalstep:  buildings[i].Totalstep,
				Step:       buildings[i].Step,
				Skin:       buildings[i].Skin,
				Showskin:   buildings[i].Showskin,
			})
		}
		// transferObject(resp, u1)
	}
	return u1, resp.UserBuilding
}

func (db *DbWrapper) UserLogin(openid string, t int) (*User, []*dtos.UserBuildingX) {
	resp := new(dtos.UserDetail)
	u1 := new(User)
	db.DbInstance.Where("openid=? and type=?", openid, t).Find(&u1)
	if u1.Id == 0 {
		// do not exist
		// generate one
		var result Result
		db.DbInstance.Raw(`SELECT random_id
		FROM (
		  SELECT FLOOR(RAND() * 99999) AS random_id 
		  FROM users
		  UNION
		  SELECT FLOOR(RAND() * 99999) AS random_id
		) AS ss
		WHERE "random_num" NOT IN (SELECT playerid FROM users)
		LIMIT 1`).Scan(&result)

		if result.RandomId == 0 {
			return nil, nil
		}
		// playerid += 20000000
		user := &User{Uname: "", Pwd: "", Openid: openid, Type: t, Playerid: result.RandomId, Manorlv: 1, Skilllv: 1}
		db.DbInstance.Create(&user)
		// 查询无前置建筑的建筑给到新用户
		var building_cfg []BuildingConfigs
		db.DbInstance.Where("pre='0'").Find(&building_cfg)
		for i := 0; i < len(building_cfg); i++ {
			userBuilding1 := &UserBuilding{
				Uid:        user.Id,
				Buildingid: building_cfg[i].Id,
				Step:       1,
				Skin:       0,
				Showskin:   0,
				Totalstep:  building_cfg[i].Steps,
			}
			db.DbInstance.Create(&userBuilding1)
			resp.UserBuilding = append(resp.UserBuilding, &dtos.UserBuildingX{
				Buildingid: building_cfg[i].Id, Id: userBuilding1.Id, Step: 1, Totalstep: building_cfg[i].Steps,
			})
		}
		// transferObject(resp, user)
		return user, resp.UserBuilding
	} else {
		var buildings []UserBuilding
		db.DbInstance.Where("uid=?", u1.Id).Find(&buildings)
		for i := 0; i < len(buildings); i++ {
			resp.UserBuilding = append(resp.UserBuilding, &dtos.UserBuildingX{
				Buildingid: buildings[i].Buildingid,
				Id:         buildings[i].Id,
				Totalstep:  buildings[i].Totalstep,
				Step:       buildings[i].Step,
				Skin:       buildings[i].Skin,
				Showskin:   buildings[i].Showskin,
			})
		}
		// transferObject(resp, u1)
	}
	return u1, resp.UserBuilding
}

func (db *DbWrapper) GotOneUserItem(uid, iid, amount, t int) {
	userItem := &UserItem{
		Uid:    uid,
		Iid:    iid,
		Amount: amount,
		T:      t,
		Sid:    0,
		Seq:    0,
		Bid:    0,
	}
	db.DbInstance.Where("uid=? and iid=?", uid, iid).Find(&userItem)
	if userItem.Id != 0 {
		// 存在,加数量
		db.DbInstance.Model(&UserItem{}).Where("id = ?", userItem.Id).Updates(map[string]interface{}{"amount": userItem.Amount + amount})
	} else {
		// 不存在,创建
		db.DbInstance.Create(&userItem)
	}

}

func (db *DbWrapper) GetUserItemList(uid int) []*UserItem {
	var userItems []*UserItem
	db.DbInstance.Where("uid=?", uid).Find(&userItems)

	return userItems
}

func (db *DbWrapper) GetUser(uid int) *User {
	u1 := new(User)
	db.DbInstance.Where("id=?", uid).First(&u1)
	return u1
}

func (db *DbWrapper) UpdateUser(uid int, lv int, exp int, coin int) int64 {
	result := db.DbInstance.Model(&User{}).Where("id = ?", uid).Updates(map[string]interface{}{"coin": coin, "manorlv": lv, "manorexp": exp})
	return result.RowsAffected
}

func (db *DbWrapper) UpdateUserByMap(id int, p map[string]interface{}) int64 {
	result := db.DbInstance.Model(&User{}).Where("id = ?", id).Updates(p)
	return result.RowsAffected
}

func (db *DbWrapper) FinishUserBuilding(uid int, finishedBuildingId int) []*dtos.UserBuildingX {
	var building_cfg []BuildingConfigs
	var ub []*dtos.UserBuildingX
	db.DbInstance.Where("FIND_IN_SET(?,pre)", strconv.Itoa(finishedBuildingId)).Find(&building_cfg)
	for i := 0; i < len(building_cfg); i++ {
		// 检测需要多少个并行条件才可以开启后面的
		preBuilding := building_cfg[i].Pre
		preArr := strings.Split(preBuilding, ",")
		conditionToOpenNext := make([]int64, len(preArr))
		for j := 0; j < len(preArr); j++ {
			conditionToOpenNext[j], _ = strconv.ParseInt(preArr[j], 10, 32)
		}
		var cnt int64
		db.DbInstance.Model(UserBuilding{}).Where("buildingid in ? and uid=? and step=totalstep", conditionToOpenNext, uid).Count(&cnt)
		if int(cnt) >= len(preArr) {
			// 前置任务都完成了
			userBuilding1 := &UserBuilding{
				Uid:        uid,
				Buildingid: building_cfg[i].Id,
				Step:       1,
				Skin:       0,
				Showskin:   0,
				Totalstep:  building_cfg[i].Steps,
			}
			db.DbInstance.Where(UserBuilding{Uid: uid, Buildingid: building_cfg[i].Id}).FirstOrCreate(&userBuilding1)
			ub = append(ub, &dtos.UserBuildingX{
				Buildingid: building_cfg[i].Id, Id: userBuilding1.Id, Step: 1, Totalstep: building_cfg[i].Steps,
			})
		}
	}
	return ub
}

func (db *DbWrapper) GetUserItemsForSkin(uid int, bid int, sid int) []*UserItem {
	var items []*UserItem
	db.DbInstance.Where("uid=? and t=1 and bid=? and sid=?", uid, bid, sid).Find(&items)
	return items
}

func (db *DbWrapper) UserUnlockSkinUseItem(uid int, updates []int, deletes []int) {
	if len(updates) > 0 {
		db.DbInstance.Model(&UserItem{}).Where("uid=? and id in ?", uid, updates).Update("amount", gorm.Expr("amount - 1"))
	}

	if len(deletes) > 0 {
		db.DbInstance.Where("uid=? and id in ?", uid, deletes).Delete(&UserItem{})
	}
}

func (db *DbWrapper) ResetUserBuilding(uid int) {
	db.DbInstance.Exec(`update user_buildings set step=1,skin=0,showskin=0 where buildingid=1 and uid=?;`, uid)
	db.DbInstance.Exec(`delete from user_buildings where uid=? and buildingid>1;`, uid)
	db.DbInstance.Exec(`update users set manorexp=0 where id=?;`, uid)
}

func (db *DbWrapper) AddCoin(uid int, coin int) {
	db.DbInstance.Exec(`update users set coin=coin+? where id=?;`, coin, uid)
}

func (db *DbWrapper) ReduceEnergy(uid int, energy int) {
	db.DbInstance.Exec(`update users set energy=energy-? where id=?;`, energy, uid)
}

func (db *DbWrapper) ExistRelation(uid int, uid2 int) *UserFriend {
	u1 := new(UserFriend)
	db.DbInstance.Where("uid=? and uid2=?", uid, uid2).First(&u1)
	return u1
}

func (db *DbWrapper) ExistRelations(uid int, uid2 int) []*UserFriend {
	var ufs []*UserFriend
	db.DbInstance.Where("(uid=? and uid2=?) or (uid=? and uid2=?)", uid, uid2, uid2, uid).Find(&ufs)
	return ufs
}

func (db *DbWrapper) CreateRelation(uid int, uid2 int, friendly int) *UserFriend {
	ufr := &UserFriend{
		Uid:      uid,
		Uid2:     uid2,
		Relation: 0,
		Friendly: friendly,
	}
	db.DbInstance.Create(&ufr)
	return ufr
}

func (db *DbWrapper) UpdateRelation(id int, p map[string]interface{}) int64 {
	result := db.DbInstance.Model(&UserFriend{}).Where("id = ?", id).Updates(p)
	return result.RowsAffected
}

func (db *DbWrapper) UpdateRelations(id []int, p map[string]interface{}) int64 {
	result := db.DbInstance.Model(&UserFriend{}).Where("id in ?", id).Updates(p)
	return result.RowsAffected
}

func (db *DbWrapper) FindRandomUsersFlowerExcludeBy(ex []int, sex, limit, zipcode int) []*UserFlowersSelect {
	var result []*UserFlowersSelect
	if len(ex) <= 0 {
		db.DbInstance.Raw(`
			select * from user_flowers as uf join (SELECT t1.id as t1id FROM users AS t1
				JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM users) - (SELECT MIN(id) FROM users)) + (SELECT MIN(id) FROM users)) AS id
			) AS t2
			WHERE
				t1.id >= t2.id  and t1.sex=? and t1.zipcode=?
			ORDER BY
				t1.id
			LIMIT ?) as tf on tf.t1id=uf.uid where uf.state=0`, sex, zipcode, limit).Scan(&result)
	} else {
		db.DbInstance.Raw(`
			select * from user_flowers as uf join (SELECT t1.id as t1id FROM users AS t1
				JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM users) - (SELECT MIN(id) FROM users)) + (SELECT MIN(id) FROM users)) AS id
			) AS t2
			WHERE
				t1.id >= t2.id and t1.id not in ? and t1.sex=? and t1.zipcode=?
			ORDER BY
				t1.id
			LIMIT ?) as tf on tf.t1id=uf.uid where uf.state=0`, ex, sex, zipcode, limit).Scan(&result)
	}
	return result
}

func (db *DbWrapper) FindRandomUsersFlowerExcludeBy2(ex []int, sex, limit, zipcode int) []*UserFlowersSelect {
	var result []*UserFlowersSelect
	if len(ex) <= 0 {
		db.DbInstance.Raw(`
			select * from user_flowers as uf join (SELECT t1.id as t1id FROM users AS t1
				JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM users) - (SELECT MIN(id) FROM users)) + (SELECT MIN(id) FROM users)) AS id
			) AS t2
			WHERE
				t1.id >= t2.id and t1.sex=? and t1.zipcode!=?
			ORDER BY
				t1.id
			LIMIT ?) as tf on tf.t1id=uf.uid where uf.state=0`, ex, sex, zipcode, limit).Scan(&result)
	} else {
		db.DbInstance.Raw(`
			select * from user_flowers as uf join (SELECT t1.id as t1id FROM users AS t1
				JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM users) - (SELECT MIN(id) FROM users)) + (SELECT MIN(id) FROM users)) AS id
			) AS t2
			WHERE
				t1.id >= t2.id and t1.id not in ? and t1.sex=? and t1.zipcode!=?
			ORDER BY
				t1.id
			LIMIT ?) as tf on tf.t1id=uf.uid where uf.state=0`, ex, sex, zipcode, limit).Scan(&result)
	}

	return result
}

func (db *DbWrapper) FindRandomUsersFlowerExcludeBy3() []*UserFlowersSelect {
	var result []*UserFlowersSelect
	db.DbInstance.Raw(`
		select * from user_flowers as uf join (SELECT t1.id as t1id FROM users AS t1
			JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM users) - (SELECT MIN(id) FROM users)) + (SELECT MIN(id) FROM users)) AS id
		) AS t2
		WHERE
			t1.id >= t2.id and t1.sex=? and t1.zipcode=?
		ORDER BY
			t1.id
		LIMIT ?) as tf on tf.t1id=uf.uid where uf.state=0`, 1, 86361000, 25).Scan(&result)

	return result
}

func (db *DbWrapper) FindUserFriendlyBetween(uid, tuid int) *UserFriend {
	u1 := new(UserFriend)
	db.DbInstance.Where("uid=? and uid2=?", uid, tuid).First(&u1)
	return u1
}

func (db *DbWrapper) FindUserSpec(uid int) *User {
	u1 := new(User)
	db.DbInstance.Select("id", "nname", "headicon", "headurl").Where("uid=?", uid).First(&u1)
	return u1
}
